package mts.groupdispatcher;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.ArrayList;

import mts.common.CommonSettings;
import mts.messages.Message;
import mts.messages.RequestMessage;
import mts.messages.StatusMessage;

import org.apache.log4j.Logger;

public class ElevatorControllerConnection extends Thread {
	private static final Logger log = Logger.getLogger(ElevatorControllerConnection.class);
	
	private RequestMessage nextRequestMessage = new RequestMessage();
	private Object mutex = new Object();
	
	private StatusMessage currentStatus;
		
	private byte car;	
	
	public synchronized void addNewHallCall(FloorRequest floorRequest) {
		nextRequestMessage.addNewHallCall(floorRequest);
	}
	
	public ArrayList<Byte> getPendingNewHallCalls() {
		return nextRequestMessage.getNewHallCalls();
	}
	
	public synchronized void addCancelledHallCall(FloorRequest floorRequest) {
		nextRequestMessage.addCancelledHallCall(floorRequest);
	}
	
	private Socket socket;
	

	public synchronized StatusMessage getCurrentStatus() {	
		if (currentStatus == null) {
			return null;
		}
		return currentStatus;		
	}
	
	private synchronized void setCurrentStatus(StatusMessage currentStatus) {
		StatusMessage old = this.currentStatus;
		this.currentStatus = currentStatus; 
		
		if (currentStatus != null) {
			if (!currentStatus.equals(old)) {
				for (StatusChangedListener listener: onStatusChangedListeners) {
					listener.update(this);
				}
			}
		}
		
		log.info("Current status message was set to: " + currentStatus.toString());
	}
	
	public byte getCarNum() {
		return car;
	}
	
	
	private ArrayList<StatusChangedListener> onStatusChangedListeners = new ArrayList<StatusChangedListener>();

	private GroupDispatcher groupDispatcher;
	
	public void onStatusChangedListener(StatusChangedListener statusUpdate) {
		onStatusChangedListeners.add(statusUpdate);
	}

	
	public ElevatorControllerConnection(GroupDispatcher groupDispatcher, Socket socket) {
		this.groupDispatcher = groupDispatcher;
		this.socket = socket;
		
		//set the initial status
		sendRequestMessage();
		setCurrentStatus((StatusMessage)receiveMessage());
		car = currentStatus.getCar();
	}
	
	private boolean shouldTerminate = false;
	
	

	@Override
	public void run() {		
	
		
		
		
		while (!shouldTerminate) {
			try {
				synchronized(mutex) {
					mutex.wait();
				}
			} catch (InterruptedException e) {
				log.error("Thread could not wait", e);
			}
			sendRequestMessage();
			setCurrentStatus((StatusMessage)receiveMessage()); // receive the response
			
			
		}
	}
	
	public void sendMessage() {
		
		synchronized (mutex) {
			mutex.notifyAll();
		}
	}
	
	
	private void sendRequestMessage() {
		byte[] data;
		String msg;
		synchronized (this) {
			 data = nextRequestMessage.serialize();
			 msg = nextRequestMessage.toString();
			 nextRequestMessage = new RequestMessage();			
		}
		
		
		try {
			OutputStream out = socket.getOutputStream();
		
			log.info("Sending message to client - " + msg);
			out.write(data);
			out.flush();
		} catch(IOException ex) {
			log.warn("Couldn't send message to client.  Closing Connection ", ex);
			groupDispatcher.removeElevatorController(this);
			shouldTerminate = true;
			
		}
	}
	
	public Message receiveMessage() {
		byte[] data = new byte[CommonSettings.MAX_PACKET_SIZE];
		
		try { 			
			InputStream in =  socket.getInputStream();			
			
			int size = in.read(data);	
			
			if (size == -1) {
				log.warn("Couldn't receive message from client. ");
				groupDispatcher.removeElevatorController(this);
				shouldTerminate = true;
			}
			
			return Message.deserialize(data);						
			
		} catch (IOException ex) {
			log.warn("Couldn't receive message from client. ", ex);
			groupDispatcher.removeElevatorController(this);
			shouldTerminate = true;
		}	
		
		return null;
	}


	
	

}
